#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/user.h>
#include "debugger.h"
#include "dwarf.h"

void print_help() {
    printf("=== Ptrace调试器使用说明 ===\n");
    printf("命令列表:\n");
    printf("  launch <程序> [参数] - 启动新程序进行调试\n");
    printf("  attach <PID>         - 附加到运行中的进程\n");
    printf("  continue             - 继续执行\n");
    printf("  step                 - 单步执行\n");
    printf("  break <地址|符号名|文件名:行号> - 设置断点\n");
    printf("  remove <地址>        - 移除断点\n");
    printf("  registers            - 显示寄存器状态\n");
    printf("  read <地址>          - 读取内存\n");
    printf("  write <地址> <数据>  - 写入内存\n");
    printf("  wait                 - 等待信号\n");
    printf("  detach               - 从进程分离\n");
    printf("  backtrace (bt)       - 显示栈回溯\n");
    printf("  print <变量名>       - 打印变量内存地址和值\n");
    printf("  disasm <地址> [指令数] - 反汇编指定地址的指令\n");
    printf("  disasm current [指令数] - 反汇编当前指令位置\n");
    printf("  disasm function <函数名> - 反汇编指定函数\n");
    printf("  info proc maps       - 显示进程内存映射\n");
    printf("  quit                 - 退出调试器\n");
    printf("  help                 - 显示此帮助\n");
    printf("\n断点设置示例:\n");
    printf("  break 0x1000          - 在地址0x1000设置断点\n");
    printf("  break main            - 在main函数设置断点\n");
    printf("  break test.c:10       - 在test.c第10行设置断点\n");
    printf("\n变量打印示例:\n");
    printf("  print my_var          - 打印变量my_var的地址和值\n");
    printf("  print counter         - 打印变量counter的地址和值\n");
    printf("============================\n");
}

int main(int argc, char *argv[]) {
    (void)argc;  // 避免未使用参数警告
    (void)argv;
    debugger_t *dbg = debugger_create();
    if (!dbg) {
        fprintf(stderr, "创建调试器失败\n");
        return 1;
    }
    
    printf("Ptrace调试器已启动\n");
    print_help();
    
    char command[256];
    char arg1[256], arg2[256], arg3[256];
    
    while (1) {
        printf("\ndebugger> ");
        fflush(stdout);
        
        if (fgets(command, sizeof(command), stdin) == NULL) {
            break;
        }
        
        // 移除换行符
        command[strcspn(command, "\n")] = 0;
        
        if (strlen(command) == 0) {
            continue;
        }
        
        // 解析命令
        int args = sscanf(command, "%255s %255s %255s", arg1, arg2, arg3);
        
        if (strcmp(arg1, "quit") == 0 || strcmp(arg1, "exit") == 0) {
            break;
        } else if (strcmp(arg1, "help") == 0) {
            print_help();
        } else if (strcmp(arg1, "launch") == 0) {
            if (args < 2) {
                printf("用法: launch <程序> [参数]\n");
                continue;
            }
            
            // 构建参数数组
            char *program_args[16] = {NULL};
            program_args[0] = arg2;
            
            int arg_count = 1;
            if (args > 2) {
                program_args[1] = arg3;
                arg_count = 2;
                
                // 解析额外参数（简单实现）
                char *token = strtok(command + strlen(arg1) + strlen(arg2) + 2, " ");
                while (token && arg_count < 15) {
                    program_args[arg_count++] = token;
                    token = strtok(NULL, " ");
                }
            }
            
            if (debugger_launch(dbg, arg2, program_args) == 0) {
                printf("程序启动成功，进程已停止在入口点\n");
            } else {
                printf("程序启动失败\n");
            }
        } else if (strcmp(arg1, "attach") == 0) {
            if (args < 2) {
                printf("用法: attach <PID>\n");
                continue;
            }
            
            pid_t pid = atoi(arg2);
            if (debugger_attach(dbg, pid) == 0) {
                printf("附加成功\n");
            } else {
                printf("附加失败\n");
            }
        } else if (strcmp(arg1, "continue") == 0) {
            if (debugger_continue(dbg) == 0) {
                debugger_wait_for_signal(dbg);
            } else {
                printf("继续执行失败\n");
            }
        } else if (strcmp(arg1, "step") == 0) {
            if (debugger_step(dbg) == 0) {
                debugger_wait_for_signal(dbg);
            } else {
                printf("单步执行失败\n");
            }
        } else if (strcmp(arg1, "break") == 0) {
            if (args < 2) {
                printf("用法: break <地址|符号名|文件名:行号>\n");
                printf("示例:\n");
                printf("  break 0x1000          - 在地址0x1000设置断点\n");
                printf("  break main            - 在main函数设置断点\n");
                printf("  break test.c:10       - 在test.c第10行设置断点\n");
                continue;
            }
            
            uintptr_t address = 0;
            
            // 检查是否是"文件名:行号"格式
            char *colon_pos = strchr(arg2, ':');
            if (colon_pos != NULL) {
                // 解析文件名和行号格式
                char filename[256];
                unsigned int line_num;
                
                // 分割文件名和行号
                size_t filename_len = colon_pos - arg2;
                strncpy(filename, arg2, filename_len);
                filename[filename_len] = '\0';
                
                if (sscanf(colon_pos + 1, "%u", &line_num) == 1) {
                    // 尝试通过行号查找地址
                    if (debugger_load_dwarf_info(dbg, dbg->program_path) == 0) {
                        uintptr_t relative_addr = debugger_find_address_by_line(dbg, filename, line_num);
                        if (relative_addr != 0) {
                            // 使用符号解析来获取实际的运行时地址
                            // 首先尝试解析main函数地址作为参考
                            uintptr_t main_addr = debugger_resolve_symbol(dbg, "main");
                            if (main_addr != 0) {
                                // 查找main函数在DWARF中的地址
                                // 使用nm命令获取main函数在文件中的偏移
                                uintptr_t main_file_offset = 0;
                                
                                // 执行nm命令查找main函数在文件中的偏移
                                char nm_command[512];
                                snprintf(nm_command, sizeof(nm_command), "nm '%s' 2>/dev/null | grep ' main$'", dbg->program_path);
                                FILE *nm_pipe = popen(nm_command, "r");
                                if (nm_pipe) {
                                    char line[256];
                                    if (fgets(line, sizeof(line), nm_pipe)) {
                                        char *token = strtok(line, " ");
                                        if (token) {
                                            main_file_offset = strtoul(token, NULL, 16);
                                            printf("Found using nm: main at offset 0x%lx\n", main_file_offset);
                                        }
                                    }
                                    pclose(nm_pipe);
                                }
                                
                                if (main_file_offset == 0) {
                                    printf("错误：无法找到main函数在文件中的偏移\n");
                                    continue;
                                }
                                
                                // 计算绝对地址：main运行时地址 + (行号地址 - main文件偏移)
                                address = main_addr + (relative_addr - main_file_offset);
                                printf("地址转换: main运行时地址=0x%lx, 行号相对地址=0x%lx, main文件偏移=0x%lx, 绝对地址=0x%lx\n", 
                                       main_addr, relative_addr, main_file_offset, address);
                                printf("找到源码位置 %s:%u 对应的地址: 相对地址 0x%lx -> 绝对地址 0x%lx\n", 
                                       filename, line_num, relative_addr, address);
                            } else {
                                printf("错误：无法解析main函数地址作为参考\n");
                                continue;
                            }
                        } else {
                            printf("错误：无法找到源码位置 %s:%u 对应的地址\n", filename, line_num);
                            printf("请确保：\n");
                            printf("  1. 程序已加载DWARF调试信息\n");
                            printf("  2. 文件名和行号正确\n");
                            printf("  3. 该行包含可执行代码\n");
                            continue;
                        }
                    } else {
                        printf("警告：无法加载DWARF调试信息，尝试使用符号解析\n");
                        // 回退到符号解析
                        char symbol_name[256];
                        snprintf(symbol_name, sizeof(symbol_name), "%s:%u", filename, line_num);
                        address = debugger_resolve_symbol(dbg, symbol_name);
                        if (address == 0) {
                            printf("错误：无法设置断点，请检查调试信息\n");
                            continue;
                        }
                    }
                } else {
                    printf("错误：无效的行号格式，请使用'文件名:行号'格式\n");
                    continue;
                }
            } else {
                // 尝试解析为十六进制地址
                char *endptr;
                address = strtoul(arg2, &endptr, 0);
                
                // 如果解析失败或不是纯数字，尝试作为符号解析
                if ((address == 0 && endptr == arg2) || (*endptr != '\0' && address == 0)) {
                    // 尝试符号解析
                    address = debugger_resolve_symbol(dbg, arg2);
                    if (address != 0) {
                        printf("解析符号 '%s' 为地址 0x%lx\n", arg2, address);
                    } else {
                        printf("错误：无法解析符号 '%s'，请检查符号名或使用十六进制地址\n", arg2);
                        continue;
                    }
                }
            }
            
            if (debugger_set_breakpoint(dbg, address) == 0) {
                printf("断点设置成功于地址 0x%lx\n", address);
                
                // 显示断点对应的源码位置（如果DWARF信息可用）
                if (dbg->dwarf_table) {
                    char filename[256];
                    unsigned int line;
                    if (debugger_find_line_by_address(dbg, address, filename, sizeof(filename), &line) == 0) {
                        printf("断点对应源码位置: %s:%u\n", filename, line);
                    }
                }
            } else {
                printf("断点设置失败\n");
            }
        } else if (strcmp(arg1, "remove") == 0) {
            if (args < 2) {
                printf("用法: remove <地址>\n");
                continue;
            }
            
            uintptr_t address = strtoul(arg2, NULL, 0);
            if (debugger_remove_breakpoint(dbg, address) == 0) {
                printf("断点移除成功\n");
            } else {
                printf("断点移除失败\n");
            }
        } else if (strcmp(arg1, "registers") == 0) {
            debugger_print_registers(dbg);
        } else if (strcmp(arg1, "read") == 0) {
            if (args < 2) {
                printf("用法: read <地址>\n");
                continue;
            }
            
            uintptr_t address = strtoul(arg2, NULL, 0);
            long data = debugger_read_memory(dbg, address);
            if (data != -1) {
                printf("地址 0x%lx 的数据: 0x%lx\n", address, data);
            } else {
                printf("读取内存失败\n");
            }
        } else if (strcmp(arg1, "write") == 0) {
            if (args < 3) {
                printf("用法: write <地址> <数据>\n");
                continue;
            }
            
            uintptr_t address = strtoul(arg2, NULL, 0);
            long data = strtoul(arg3, NULL, 0);
            if (debugger_write_memory(dbg, address, data) == 0) {
                printf("写入内存成功\n");
            } else {
                printf("写入内存失败\n");
            }
        } else if (strcmp(arg1, "wait") == 0) {
            debugger_wait_for_signal(dbg);
        } else if (strcmp(arg1, "detach") == 0) {
            if (debugger_detach(dbg) == 0) {
                printf("分离成功\n");
            } else {
                printf("分离失败\n");
            }
        } else if (strcmp(arg1, "backtrace") == 0 || strcmp(arg1, "bt") == 0) {
            debugger_print_backtrace(dbg);
        } else if (strcmp(arg1, "print") == 0) {
            if (args < 2) {
                printf("用法: print <变量名>\n");
                printf("示例: print my_var - 打印变量my_var的地址和值\n");
                continue;
            }
            
            debugger_print_variable_address(dbg, arg2);
        } else if (strcmp(arg1, "disasm") == 0 || strcmp(arg1, "disassemble") == 0) {
            if (args < 2) {
                printf("用法:\n");
                printf("  disasm <地址> [指令数] - 反汇编指定地址的指令\n");
                printf("  disasm current [指令数] - 反汇编当前指令位置\n");
                printf("  disasm function <函数名> - 反汇编指定函数\n");
                continue;
            }
            
            if (strcmp(arg2, "current") == 0) {
                size_t count = 10; // 默认10条指令
                if (args > 2) {
                    count = atoi(arg3);
                    if (count == 0) count = 10;
                }
                debugger_disassemble_current(dbg, count);
            } else if (strcmp(arg2, "function") == 0) {
                if (args < 3) {
                    printf("用法: disasm function <函数名>\n");
                    continue;
                }
                debugger_disassemble_function(dbg, arg3);
            } else {
                // 解析为地址
                uintptr_t address = strtoul(arg2, NULL, 0);
                size_t count = 10;
                if (args > 2) {
                    count = atoi(arg3);
                    if (count == 0) count = 10;
                }
                debugger_disassemble(dbg, address, count);
            }
        } else if (strcmp(arg1, "info") == 0) {
            if (args < 2) {
                printf("用法: info <子命令>\n");
                printf("可用子命令:\n");
                printf("  proc maps - 显示进程内存映射信息\n");
                continue;
            }
            
            if (strcmp(arg2, "proc") == 0) {
                if (args < 3) {
                    printf("用法: info proc <子命令>\n");
                    printf("可用子命令:\n");
                    printf("  maps - 显示进程内存映射信息\n");
                    continue;
                }
                
                if (strcmp(arg3, "maps") == 0) {
                    if (debugger_print_proc_maps(dbg) == 0) {
                        printf("内存映射信息显示成功\n");
                    } else {
                        printf("内存映射信息显示失败\n");
                    }
                } else {
                    printf("未知的info proc子命令: %s\n", arg3);
                }
            } else {
                printf("未知的info子命令: %s\n", arg2);
            }
        } else {
            printf("未知命令: %s (输入 'help' 查看帮助)\n", arg1);
        }
    }
    
    // 清理
    if (dbg->pid != 0) {
        debugger_detach(dbg);
    }
    debugger_destroy(dbg);
    
    printf("调试器已退出\n");
    return 0;
}